package com.miyake.dsp.osa;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.ArrayUtils;

import com.miyake.dsp.ComplexArray;
import com.miyake.dsp.analyzer.PeakSearch;
import com.miyake.dsp.analyzer.PeakSearchCommon;
import com.miyake.dsp.analyzer.PeakSearchSetting;
import com.miyake.dsp.analyzer.Threshold;
import com.miyake.dsp.analyzer.ThresholdSetting;
import com.miyake.dsp.common.UnitConverter;
import com.miyake.dsp.common.CommonTool;
import com.miyake.dsp.osa.method.Advanced;
import com.miyake.dsp.osa.method.IEC;
import com.miyake.dsp.osa.method.NFCalc;
import com.miyake.dsp.osa.method.SpectDivOff;
import com.miyake.dsp.osa.method.SpectDivOn;
import com.miyake.dsp.osa.method.Yokogawa;
import com.miyake.dsp.osa.ui.ActualResolutionUI;
import com.miyake.dsp.osa.ui.AnalysisSettingUI;
import com.miyake.dsp.osa.ui.OsaChartUI;
import com.miyake.dsp.osa.ui.ResultUI;
import com.miyake.dsp.osa.ui.WdmSettingUI;
import com.miyake.dsp.ui.LineChartPanel;
import com.thoughtworks.xstream.XStream;

public class OsaTestTool {

	public static void main(String[] arg) {
		new OsaTestTool();
	}
	enum RoundType {
		NoRound, Anritsu, Yokogawa
	};
	private WdmProperty wdmProp;// = new WdmProperty();
	private AnalysisProperty anaProp;// = new AnalysisProperty();
	private Map<Double, Map<Integer, Double>> actresTable;

	private double[] x;
	private OsaSetting osaSetting;
	private LineChartPanel chart;// = LineChartPanel.createChart();
	private double[]in;
	private double[]out;
	private List<List<Double>> traces = new ArrayList<List<Double>>();
	private ResultUI resultUI;
	private AnalysisSettingUI settingUI;
	private WdmSettingUI wdmUI;
	private OsaChartUI chartUI;
	private ActualResolutionUI actresUI;

	private RbwFactoryDefault rbwFactoryDefault;
	private Map<Double, Double> fixedSpan;

	public OsaTestTool() {
		LogTrace.setLogEnabled(false);
		rbwFactoryDefault = new RbwFactoryDefault();

		if (!readPropertyFile("properties.tst")) {
			createDefaultWdmProperty();
			createDefaultAnalysisProperty();
			createDefaultActresTable();
			createDefaultFixedSpan();
		}

		resultUI = createResultUI();
		resultUI.setVisible(true);

		chartUI = new OsaChartUI() {

			@Override
			public void onWdmSource() {
				if (wdmUI == null) {
					wdmUI = createWdmSettingUI();
				}
				wdmUI.setVisible(true);
			}

			@Override
			public void onAnalysis() {
				if (settingUI == null) {
					settingUI = createAnalysisSettingUI();
				}
				settingUI.setVisible(true);
			}

			@Override
			public void onResultTable() {
				if (resultUI == null) {
					resultUI = createResultUI();
				}
				resultUI.setVisible(true);
			}

			@Override
			public void onSaveXML(String path) {
				saveXML(path);
				writePropertyFile(path);
			}

			@Override
			public void onSaveSetting(String path) {
				writePropertyFile(path);
			}

			@Override
			public void onRecallSetting(String path) {
				readPropertyFile(path);
				if (settingUI != null) {
					settingUI.setVisible(false);
					settingUI = null;
				}
				if (wdmUI != null) {
					wdmUI.setVisible(false);
					wdmUI = null;
				}
				analyze();
			}

			@Override
			public void onActualResolution() {
				if (actresUI == null) {
					actresUI = createActresUI();
				}
				actresUI.setVisible(true);

			}

			@Override
			public void onSaveText(String path) {
				saveText(path + "_in.txt", in);
				saveText(path + "_out.txt", out);
			}

			@Override
			public void onSaveTable(String path) {
				OsaTestTool.this.saveTable(path);
			}

			@Override
			public void onSaveYokogawa(String path) {
				OsaTestTool.this.saveYokogawa(path);
			}

		};

		chartUI.setVisible(true);
		chart = chartUI.getChart();

		try {
			analyze();
		}
		catch(Exception e) {
			e.printStackTrace();
			LogTrace.writeLog(e.toString());
		}
	}

	protected void saveYokogawa(String path) {
		new YokogawaConverter(this.osaSetting, x, in, path+"_in.csv");
		new YokogawaConverter(this.osaSetting, x, out, path+"_out.csv");

	}

	private void createDefaultFixedSpan() {
		fixedSpan = new HashMap<Double, Double>();
		fixedSpan.put(0.03, 0.5);
		fixedSpan.put(0.05, 0.53);
		fixedSpan.put(0.07, 0.6);
		fixedSpan.put(0.1, 0.65);
		fixedSpan.put(0.2, 0.8);
		fixedSpan.put(0.5, 1.3);
		fixedSpan.put(1.0, 2.2);
	}

	private void createDefaultActresTable() {
		actresTable = new HashMap<Double, Map<Integer, Double>>();
		Map<Integer, Double> e1 = new HashMap<Integer, Double>();
		e1.put(600, 0.03);
		e1.put(1000, 0.03);
		e1.put(1400, 0.03);
		e1.put(1650, 0.03);
		actresTable.put(0.03, e1);

		Map<Integer, Double> e2 = new HashMap<Integer, Double>();
		e2.put(600, 0.05);
		e2.put(1000, 0.05);
		e2.put(1400, 0.05);
		e2.put(1650, 0.05);
		actresTable.put(0.05, e2);

		Map<Integer, Double> e3 = new HashMap<Integer, Double>();
		e3.put(600, 0.07);
		e3.put(1000, 0.07);
		e3.put(1400, 0.07);
		e3.put(1650, 0.07);
		actresTable.put(0.07, e3);

		Map<Integer, Double> e4 = new HashMap<Integer, Double>();
		e4.put(600, 0.1);
		e4.put(1000, 0.1);
		e4.put(1400, 0.1);
		e4.put(1650, 0.1);
		actresTable.put(0.1, e4);

		Map<Integer, Double> e5 = new HashMap<Integer, Double>();
		e5.put(600, 0.2);
		e5.put(1000, 0.2);
		e5.put(1400, 0.2);
		e5.put(1650, 0.2);
		actresTable.put(0.2, e5);

		Map<Integer, Double> e6 = new HashMap<Integer, Double>();
		e6.put(600, 0.5);
		e6.put(1000, 0.5);
		e6.put(1400, 0.5);
		e6.put(1650, 0.5);
		actresTable.put(0.5, e6);

		Map<Integer, Double> e7 = new HashMap<Integer, Double>();
		e7.put(600, 1.0);
		e7.put(1000, 1.0);
		e7.put(1400, 1.0);
		e7.put(1650, 1.0);
		actresTable.put(1.0, e7);
	}

	private ResultUI createResultUI() {
		return new ResultUI();
	}

	private void createDefaultAnalysisProperty() {
		anaProp = new AnalysisProperty();
		anaProp.setMethod("IEC");
		anaProp.setFittingSpan(0.76);
		anaProp.setMaskedSpan(0.40);
		anaProp.setFormula("y=x(1) + x(2)*t");
		anaProp.setRbw("Measured");
		anaProp.setRbwValue(0.095);
		anaProp.setNfcalc("S-ASE");
		anaProp.setPeakThreshold(0.1);
		anaProp.setRealSpan(true);
		anaProp.setK(1.0);
		anaProp.setSliceLevel(10);
		anaProp.setLog(true);
		anaProp.setPeak(true);
		anaProp.setCutLevel(3.0);
		anaProp.setSpanMethod("Center");
	}

	private void createDefaultWdmProperty() {
		wdmProp = new WdmProperty();
		wdmProp.setSpacing(0.8);
		wdmProp.setPeakCount(2);
		wdmProp.setInFile("data\\WaveData20111216_002.csv");
		wdmProp.setOutFile("data\\WaveData20111216_003.csv");
		wdmProp.setPoints(1001);
		wdmProp.setAse(false);
		wdmProp.setAseValue(0.1);
		wdmProp.setGain(false);
		wdmProp.setGainValue(10);
		wdmProp.setCenter(1550);
		wdmProp.setSpan(50);
		wdmProp.setChannelProperty(new ChannelProperty());
	}

	private WdmSettingUI createWdmSettingUI() {
		WdmSettingUI wdmUI = new WdmSettingUI(wdmProp) {
			@Override
			public void onSetClick(WdmProperty prop) {
				wdmProp = prop;
				analyze();
			}

		};
		return wdmUI;
	}

	private AnalysisSettingUI createAnalysisSettingUI() {
		AnalysisSettingUI settingUI = new AnalysisSettingUI(anaProp) {
			@Override
			public void onSetClick(AnalysisProperty prop) {
				anaProp = prop;
				analyze();
			}
		};
		return settingUI;
	}

	private boolean readPropertyFile(String filename) {
		try {
			BufferedReader  reader = new BufferedReader (new FileReader(new File(filename)));
			XStream xstream = new XStream();
			String xml = "";
			String s;
			while((s = reader.readLine()) != null) {
				xml += s;
			}
			SaveObject obj = (SaveObject)xstream.fromXML(xml);
			this.anaProp = obj.ana;
			this.wdmProp = obj.wdm;
			this.actresTable = obj.actres;
//			this.fixedSpan = obj.fixedSpan;
			if (actresTable == null) {
				createDefaultActresTable();
			}
			if (fixedSpan == null) {
				createDefaultFixedSpan();
			}
			reader.close();
			return true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			LogTrace.writeLog(e.toString());
		} catch (IOException e) {
			e.printStackTrace();
			LogTrace.writeLog(e.toString());
		}
		return false;
	}

	private void writePropertyFile(String filename) {
		if (!filename.endsWith(".tst")) {
			filename += ".tst";
		}
		XStream xstream = new XStream();
		String xml = xstream.toXML(new SaveObject(this.wdmProp, this.anaProp, this.actresTable));
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filename)));
			writer.write(xml);
			writer.flush();
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
			LogTrace.writeLog(e.toString());
		}
	}

	class SpanCreatetor2 {

		public double getLeft() {
			return left;
		}

		public double getRight() {
			return right;
		}

		double left;
		double right;

		int[] peaks;

		private RoundType roundType;

		public SpanCreatetor2(int[] peaks, RoundType roundType) {
			this.peaks = peaks;
			this.roundType = roundType;
		}

		public void setIndex(int i) {
			int peakIndex = peaks[i];
			double center = getCenter(peakIndex);

			if (anaProp.getSpanMethod().equals("Center")) {
				if (i == 0) {
					left =  getCenter(peaks[0]) - (getCenter(peaks[1])-getCenter(peaks[0]))/2.0;
				}
				else {
					left = getCenter(peaks[i]) - (getCenter(peaks[i]) - getCenter(peaks[i - 1]))/2.0;
				}

				if (i == peaks.length-1) {
					int end = peaks.length-1;
					right =  getCenter(peaks[end]) + (getCenter(peaks[end]) - getCenter(peaks[end-1]))/2.0;
				}
				else {
					right = getCenter(peaks[i]) + (getCenter(peaks[i + 1]) - getCenter(peaks[i])) / 2.0;
				}
			}
			else if (anaProp.getSpanMethod().equals("Manual")){ // Manual
				left = center - anaProp.getFittingSpan() / 2.0;// - 0.022;
				right = center + anaProp.getFittingSpan() / 2.0;// + 0.023;
			}
			else if (anaProp.getSpanMethod().equals("Res")) {
				left = center - fixedSpanHalf();
				right = center + fixedSpanHalf();
			}

		}
	};


	private void analyze() {
		// Clears chart and results
		chart.removeAllTrace();
		resultUI.clear();
		resultUI.setAnalysisProperty(anaProp);
		traces.clear();

		// Actual Resolution Table
		rbwFactoryDefault.setData(actresTable);

		// Selects method
		NFCalc calc = createMethod();

		// Creates WDM source data
		//createWdmSource();
		createWdmSource2();

		// Search peaks
		int[] inPeaks = getInPeaks();
		int[] outPeaks = getOutPeaks();
		if (inPeaks.length != outPeaks.length)  {
			outPeaks = inPeaks;
		}
		// Adds in & out data to chart
		setDataToChart("in", x, in);
		setDataToChart("out", x, out);

		// List for interpolated data
		List<Double> xFitArr = new ArrayList<Double>();

		// Initializes fitting / masked span
		/*
		inPeaks = new int[21];
		inPeaks[0] = 213;
		inPeaks[1] = 258;
		inPeaks[2] = 321;
		inPeaks[3] = 393;
		inPeaks[4] = 463;
		inPeaks[5] = 530;
		inPeaks[6] = 605;
		inPeaks[7] = 673;
		inPeaks[8] = 746;
		inPeaks[9] = 810;
		inPeaks[10] = 883;
		inPeaks[11] = 959;
		inPeaks[12] = 1027;
		inPeaks[13] = 1092;
		inPeaks[14] = 1170;
		inPeaks[15] = 1239;
		inPeaks[16] = 1317;
		inPeaks[17] = 1382;
		inPeaks[18] = 1453;
		inPeaks[19] = 1526;
		inPeaks[20] = 1597;

		outPeaks = new int[21];
		outPeaks[0] = 213;
		outPeaks[1] = 256;
		outPeaks[2] = 322;
		outPeaks[3] = 393;
		outPeaks[4] = 462;
		outPeaks[5] = 531;
		outPeaks[6] = 607;
		outPeaks[7] = 673;
		outPeaks[8] = 746;
		outPeaks[9] = 810;
		outPeaks[10] = 883;
		outPeaks[11] = 957;
		outPeaks[12] = 1027;
		outPeaks[13] = 1092;
		outPeaks[14] = 1169;
		outPeaks[15] = 1239;
		outPeaks[16] = 1317;
		outPeaks[17] = 1382;
		outPeaks[18] = 1451;
		outPeaks[19] = 1526;
		outPeaks[20] = 1597;
*/

		// Creates fitting / masked span creator.
		SpanCreatetor2 spanCreator = new SpanCreatetor2(inPeaks, RoundType.Yokogawa);

		// List for peak / fitting / masked markers
		List<Double> markers = new ArrayList<Double>();

		for (int i = 0; i < inPeaks.length; i++) {
			int inPeakIndex = inPeaks[i];
			int outPeakIndex = outPeaks[i];//outPeaks[i];
			int peakCenterIndex;

			double center = getCenter(inPeakIndex);
			//double center = getCenter(outPeakIndex);

			// Gets fitting / masked span
			spanCreator.setIndex(i);

			anaProp.setFittingStart(spanCreator.getLeft());
			anaProp.setFittingEnd(spanCreator.getRight());
			// Creates fitting settings

			int inThreshPeakIndexMin;
			int inThreshPeakIndexPul;
			/* Threshold */
			inThreshPeakIndexMin = CommonTool.getApproximateCenterIndex( x, center - osaSetting.getSettingResolution() / 2 );
			inThreshPeakIndexPul = CommonTool.getApproximateCenterIndex( x, center + osaSetting.getSettingResolution() / 2 );

			peakCenterIndex = CommonTool.getApproximateCenterIndex( x, center );
			outPeakIndex = CommonTool.getPeakFromArea( out, inThreshPeakIndexMin, inThreshPeakIndexPul );

			// Calculates NF
			calc.setData(x, in, out, anaProp, anaProp.isFitInLog(),
					inPeakIndex, outPeakIndex, peakCenterIndex, center);

			double nf = getNf(calc, inPeaks, i, anaProp.getK());
			resultUI.addResult(
					center,
					in[inPeakIndex],
					out[outPeakIndex],
					calc.getAse(),
					calc.getGain(),
					nf,
					getRbw(inPeaks, i),
					calc.getFittingStart(),
					calc.getFittingEnd(),
					calc.getMaskedStart(),
					calc.getMaskedEnd()
			);

			int j = 0;
			for (double[] trace : calc.getTraces()) {
				addTrace(j++, trace);
			}
			addToList(xFitArr,  calc.getSubX());

			// Adds fitting markers to list
			if (isFittinSpanVisible()) {
				markers.add(calc.getFittingStart());
				markers.add(calc.getFittingEnd());
			}
			if (isMaskedSpanVisible()) {
				markers.add(calc.getMaskedStart());
				markers.add(calc.getMaskedEnd());
			}

			// Adds peak markers to list
			if (isPeakVisible()) {
				markers.add(center);
			}
		}

		// Shows interpolated data
		int num = 1;
		for (List<Double> t : traces) {
			setDataToChart("Intrp#"+String.valueOf(num++), ArrayUtils.toPrimitive(xFitArr.toArray(new Double[0])),ArrayUtils.toPrimitive(t.toArray(new Double[0])));
		}

		// Shows markers
		if (isMarkerVisible()) {
			chart.addXMarker(markers);
		}

		// Saves current settings
		writePropertyFile("properties.tst");
	}

	private double getCenter(int peakIndex) {
		double center;
		if (anaProp.isPeak()) {
			center = toValue(peakIndex);
		}
		else {
			center = getThresholdCenter(peakIndex, in);
		}
		return center;
	}

	private double getThresholdCenter(int peakIndex, double[] trace) {
		// Threshold
		Threshold threshold = new Threshold(new ThresholdSetting(anaProp.getCutLevel(), peakIndex, osaSetting.getStartWavelength(), osaSetting.getStopWavelength()));
		threshold.setSource(new ComplexArray(trace));
		threshold.getProcessed();
		return threshold.getCenter();
	}

	private int updatePeakIndex(int peakIndex) {
		return toIndex(getCenter(peakIndex));
	}

	private void setDataToChart(String title, double[] x, double[] y) {
		if (anaProp.isLog()) {
			chart.setData(title, x, UnitConverter.todBm(y));
		}
		else {
			chart.setData(title, x, y);
		}
	}

	private int[] getInPeaks() {
		return getPeaks(in);
	}

	private int[] getOutPeaks() {
		return getPeaks(out);
	}

	private int[] getPeaks(double[] data) {
/*		PeakSearch peakSearch = new PeakSearch(new PeakSearchSetting(anaProp.getSliceLevel(), anaProp.getPeakThreshold()));
		peakSearch.setSource(new ComplexArray(data));
		double[] dpeaks = peakSearch.getProcessed().getRealPart();
		int[] peaks = new int[dpeaks.length];
		for (int i = 0; i < dpeaks.length; i++) {
			peaks[i] = (int)dpeaks[i];
		}
		return peaks;
*/
		PeakSearchCommon peakSearch = new PeakSearchCommon(data);
				//, anaProp.getSliceLevel(), anaProp.getPeakThreshold());

		return ArrayUtils.toPrimitive(peakSearch.getPeakInexes().toArray(new Integer[0]));
	}


	private void roundByDbm(double[] d) {
		for (int i = 0; i < d.length; i++) {
			double v = d[i];
			if (v > 0.0) {
				double dbm = 10 * Math.log10(v*1000.0);
				double rounded= Math.round(dbm*1000)/1000.0;
				double newvalue = Math.pow(10.0, rounded/10.0)/1000.0;
				d[i] = newvalue;
			}
			else {
				d[i] = Math.pow(10.0, -90.0/10.0)/1000.0;
			}
		}
	}

	private void createWdmSource2() {
		WdmGenerator2 wdm = new WdmGenerator2(wdmProp);
		in = wdm.getIn();
		out = wdm.getOut();

		roundByDbm(in);
		roundByDbm(out);

		// Adds Pin Loss/ Pout Loss to trace data
		in = addLoss(in, anaProp.getPinLos());
		out = addLoss(out, anaProp.getPoutLoss());

		x = wdm.getWavelength();
		osaSetting = wdm.getOsaSetting();
	}

	private void createWdmSource() {
		OsaCsv csvIn = new OsaCsv(wdmProp.getInFile());
		OsaCsv csvOut = new OsaCsv(wdmProp.getOutFile());

		osaSetting = csvIn.getSetting();

		WdmGenerator inWDM = new WdmGenerator(csvOut.getLevel(), osaSetting.getSpanWavelength());

		inWDM.setWhiteNoiseEnabled(wdmProp.isWhiteNoise());
		inWDM.setWhiteNoiseValue(wdmProp.getWhiteNoiseValue());

		in  = inWDM.getWdm(wdmProp.getPoints(), wdmProp.getSpacing(),
				wdmProp.getPeakCount());


		WdmGenerator outWDM = new WdmGenerator(csvIn.getLevel(), osaSetting.getSpanWavelength(),
				inWDM.getPeakIndex());
		outWDM.setAseEnabled(wdmProp.isAse());
		outWDM.setAseValue(wdmProp.getAseValue());
		outWDM.setAseFlat(wdmProp.isAseFlat());
		outWDM.setGainEnabled(wdmProp.isGain());
		outWDM.setGainValue(wdmProp.getGainValue());
		outWDM.setGainFlat(wdmProp.isGainFlat());
		outWDM.setWhiteNoiseEnabled(wdmProp.isWhiteNoise());
		outWDM.setWhiteNoiseValue(wdmProp.getWhiteNoiseValue());

		out =  outWDM.getWdm(wdmProp.getPoints(), wdmProp.getSpacing(),
				wdmProp.getPeakCount());

		// Adds Pin Loss/ Pout Loss to trace data
		in = addLoss(in, anaProp.getPinLos());
		out = addLoss(out, anaProp.getPoutLoss());

		x = expandX(csvIn.getX(), wdmProp.getPoints());

		// Updates osa setting properties

		osaSetting.setSamplingPoints(in.length);
		osaSetting.setStartWavelength(x[0]);
		osaSetting.setStopWavelength(x[x.length-1]);

		// Test
//		if (wdmProp.getChannelProperty() != null) {
//			double[] test = inWDM.getWdmCustom(this.wdmProp.getChannelProperty());
//			System.out.println(test.length);
//		}
	}

	private double[] addLoss(double[] data, double loss) {
		double[] ret = new double[data.length];
		double c = UnitConverter.toLinear(loss);
		for (int i = 0; i < data.length; i++) {
			ret[i] = data[i] * c;
		}
		return ret;
	}

	private int round(int value) {
		if (value > in.length) {
			return in.length - 1;
		}
		else if (value < 0) {
			return 0;
		}
		return value;
	}

	public static double toVacuum(double wavelength) {
		double refractiveIndex = 1.0002582551889045;
		wavelength = Double.valueOf(String.format("%.4f", wavelength*refractiveIndex));
		return wavelength;
	}

	private double getNf(NFCalc calc, int[] peaks,
			int index, double k) {
		double nf;
		double center = getCenter(peaks[index]);
		double res = getRbw(peaks, index);

		if (osaSetting.isAir()) {
			center = toVacuum(center);
		}

		if (anaProp.getNfcalc().equalsIgnoreCase("Total")) {
			nf = NoiseFigure.calcNFTotal(
					calc.getAse() * UnitConverter.toLinear(anaProp.getBpfLevel()),
					calc.getGain(),
					center,
					res,
					k,
					in[toIndex(center)],
					anaProp.getBpfBandwidth());
		}
		else {
			nf = NoiseFigure.calcNF(
					calc.getAse()* UnitConverter.toLinear(anaProp.getBpfLevel()),
					calc.getGain(),
					center,
					res,
					k);
		}
		return nf;
	}
	private int getRightCenter(int[] peaks, int index, boolean roundUp) {
		double c = getCenter(peaks[index]);
		double value;

		if (index == peaks.length - 1) {
			//cright = c + fixedSpanHalf() * 2.0;
			//return round(toIndexRoundDown(c + fixedSpanHalf()));
			value = c + fixedSpanHalf();
		}
		else {
			double cright = getCenter(peaks[index+1]);
			//return round(toIndexRoundDown((c + cright) / 2.0));
			value = (c + cright) / 2.0;
		}

		if (roundUp) {
			return (int)round(toIndexRoundUp(value));
		}
		else {
			return (int)round(toIndexRoundDown(value));
		}
	}

	private int getLeftCenter(int[] peaks, int index, boolean roundUp) {
		double c = getCenter(peaks[index]);
		double value;
		if (index == 0) {
			//return round(toIndexRoundUp(c - fixedSpanHalf()));
			value = c - fixedSpanHalf();
		}
		else {
			double cleft = getCenter(peaks[index-1]);
			//return round(toIndexRoundUp((c + cleft) / 2.0));
			value = (c + cleft) / 2.0;
		}

		if (roundUp) {
			return (int)round(toIndexRoundUp(value));
		}
		else {
			return (int)round(toIndexRoundDown(value));
		}
	}

	private double getRbw(int[] peaks, int index) {
		int peakIndex = peaks[index];
		if (anaProp.getRbw().equals("Initial")) {
			return rbwFactoryDefault.getRBW(osaSetting.getSettingResolution(), toValue(peakIndex));
		}
		else if (anaProp.getRbw().equals("Measured")){
			return getRrbDynamic(peaks, index);
		}
		else {
			return anaProp.getRbwValue();
		}
	}

	private double getRrbDynamic(int[] peaks, int index) {
//		System.out.print("No. "+String.valueOf(index+1));
		int peakIndex = updatePeakIndex(peaks[index]);
//		System.out.print(" Org Peak=" + peaks[index] + "New Peak="+ peakIndex);
		int left = getLeftCenter(peaks, index, false);
		int right = getRightCenter(peaks, index, false);
//		double actres = osaSetting.getActualResolution();
//		System.out.println(" Left=" + (left) + "\tRight=" + (right));
		double s = getIntegral(left, right, in);
//		System.out.print(",s="+s);

		double p = in[peakIndex];// / actres;
//		System.out.print(",p="+p);
//		System.out.print(",w=" + (s/p));

//		System.out.print("leftwav="+toValue(left)+" rightwav=" + toValue(right));
//		System.out.println();
		return s / p;
	}

	private double getIntegral(int start, int end, double[] pow) {
		double ret = 0;
		double rbw = osaSetting.getActualResolution();
		double deltaLambda = (x[x.length-1] - x[0]) / (x.length - 1);

//		double deltaLambda2 = (getCenter(end)-getCenter(start)) / (end - start-1);
//		System.out.print(";dLambda=" + deltaLambda + "," + deltaLambda2);
		//System.out.print(";length=" + pow.length);
		for (int i = start; i <= end; i++) {
			ret += pow[i] * deltaLambda;// / rbw;
//			System.out.println(pow[i]);
		}
//		System.out.println(";Integ=" + ret);
		return ret;
	}

	private NFCalc createMethod() {
		if (anaProp.getMethod().equals("IEC")) {
			return new IEC();
		}
		else if (anaProp.getMethod().equals("Advanced")) {
			return new Advanced();
		}
		else if (anaProp.getMethod().equals("SpectDivOn")) {
			return new SpectDivOn();
		}
		else if (anaProp.getMethod().equals("SpectDivOff")) {
			return new SpectDivOff();
		}
		else if (anaProp.getMethod().equals("Yokogawa")) {
			return new Yokogawa();
		}
		return null;
	}

	private void addTrace(int i, double[] trace) {
		if (i >= traces.size()) {
			traces.add(new ArrayList<Double>());
		}
		List<Double> list = traces.get(i);
		for (double d : trace) {
			list.add(d);
		}
	}

	private boolean isPeakVisible() {
		return true;
	}
	private boolean isMaskedSpanVisible() {
		return anaProp.isMaskedVisible();
	}
	private boolean isFittinSpanVisible() {
		return anaProp.isFittingVisible();
	}
	private double[] extract(double[] x2, int fittingStart, int fittingEnd) {
		double[] ret = new double[fittingEnd - fittingStart + 1];
		try {
			for (int i = 0; i < ret.length; i++) {
				ret[i] = x2[i + fittingStart];
			}
		}
		catch (Exception e) {
			e.printStackTrace();
			LogTrace.writeLog(e.toString());
		}
		return ret;
	}
	private void addToList(List<Double> xarr, double[] fit2) {
		for (int j = 0; j < fit2.length; j++) {
			xarr.add(fit2[j]);
		}
	}
	private boolean isMarkerVisible() {
		return true;
	}
	private double[] expandX(double[] x2, int points) {
		double step = (x2[x2.length-1] - x2[0]) / (double)(x2.length - 1);
		double[] ret = new double[points];
		for (int i = 0; i < ret.length; i++) {
			ret[i] = x2[0] /*- (step * points/2)*/ + step * (double)i;
		}
		return ret;
	}
	private boolean isLog() {
		return anaProp.isLog();
	}

	private int toSpanIndex(double fittingSpan) {
		double step = (osaSetting.getStopWavelength() - osaSetting.getStartWavelength()) / (double)(x.length-1);
		return (int) (fittingSpan / step);
	}

	private double toValue(int index) {
		return osaSetting.getStartWavelength() + (osaSetting.getStopWavelength() - osaSetting.getStartWavelength()) * (double)index / (double)(x.length-1);
	}

	private double getRealIndex(double wavelength) {
		double real = (wavelength-osaSetting.getStartWavelength()) * (double)(osaSetting.getSamplingPoints() - 1) /
				(osaSetting.getStopWavelength()-osaSetting.getStartWavelength());

		return roundWavelength(real);

	}
	private double roundWavelength(double wavelength) {
		return Double.valueOf(String.format("%.4f", wavelength));
	}

	private int toIndex(double wavelelngth) {
		return (int)Math.round(getRealIndex(wavelelngth));
	}

	private int toIndexRoundUp(double wavelength) {
		return (int)Math.ceil(getRealIndex(wavelength));
	}
	private int toIndexRoundDown(double wavelength) {
		return (int) Math.floor(getRealIndex(wavelength));
	}
	protected void saveXML(String filename) {
		List<double[]> traces = new ArrayList<double[]>();
		traces.add(in);
		traces.add(out);
		OsaXml xml = new OsaXml(osaSetting, anaProp, traces);
		if (!filename.substring(filename.length()-3, filename.length()).equalsIgnoreCase("xml")) {
			filename += ".xml";
		}
		xml.writeXML(filename);

	}

	protected void saveTable(String filename) {
		resultUI.save(filename);
	}

	protected void saveText(String filename, double[] data) {
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filename)));
			for (int i = 0; i < data.length; i++) {
				writer.write(String.valueOf(data[i]));
				writer.newLine();
			}
			writer.flush();
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private ActualResolutionUI createActresUI() {
		ActualResolutionUI ui = new ActualResolutionUI(actresTable) {

			@Override
			public void onSet(Map<Double, Map<Integer, Double>> aMap) {
				actresTable = aMap;
				analyze();
			}

		};
		return ui;
	}

	private double fixedSpanHalf() {
		return fixedSpan.get(osaSetting.getSettingResolution());
	}
};

class SaveObject {

	public SaveObject(WdmProperty wdm, AnalysisProperty ana, Map<Double, Map<Integer, Double>> actres) {
		super();
		this.wdm = wdm;
		this.ana = ana;
		this.actres = actres;
	}
	WdmProperty wdm;
	AnalysisProperty ana;
	Map<Double, Map<Integer, Double>> actres;
	Map<Double, Double> fixedSpan;
};
